﻿using System;

namespace Taste.Internal
{
    internal class CurrentState<TState, TTrigger> : ICurrentStateImpl<TState, TTrigger>
        where TState : struct
        where TTrigger : struct
    {
        private StateMachine<TState, TTrigger> _machine;
        private Action<IStateError<TState>> _errorHandler;
        private TriggerEffectCollection<TTrigger> _triggerEffects;
        private Action _actionWhenLeaving;
        private StateDefinition<TState, TTrigger> _definition;

        public CurrentState(StateMachine<TState, TTrigger> machine, StateDefinition<TState, TTrigger> definition, TState? previousStateName, object userData)
        {
            _machine = machine;
            _definition = definition;
            this.PreviousStateName = previousStateName;
            this.UserData = userData;
            _triggerEffects = new TriggerEffectCollection<TTrigger>();
        }

        public TState? PreviousStateName { get; set; }
        public TState Name { get { return _definition.Name; } }
        public object UserData { get; set; }

        public bool Leave(object userData = null)
        {
            return _machine.LeaveState(this.Name, userData);
        }

        public void ReportError(Exception error)
        {
            _machine.ErrorHappened(this.Name, error);
        }

        public void ErrorHappened(IStateError<TState> error)
        {
            if(_errorHandler != null)
                _errorHandler(error);
        }

        public void OnFired(TTrigger triggerName, Action effect, Func<bool> condition = null)
        {
            _triggerEffects.SetEffect(triggerName, effect, condition);
            _machine.TriggerEffectChanged(triggerName);
        }

        public void OnFired<TParam>(TTrigger triggerName, Action<TParam> effect, Func<TParam, bool> condition = null)
        {
            _triggerEffects.SetEffect(triggerName, effect, condition);
            _machine.TriggerEffectChanged(triggerName);
        }

        public void Ignore(TTrigger triggerName)
        {
            this.OnFired(triggerName, () => { }, () => false);
        }

        public void BeforeNextState(Action action)
        {
            _actionWhenLeaving = action;
        }

        public void OnError(Action<IStateError<TState>> action)
        {
            _errorHandler = action;
        }

        public bool? CanFire(TTrigger triggerName, object parameter)
        {
            return _triggerEffects.CanFire(triggerName, parameter);
        }

        public bool Fire(TTrigger triggerName, object parameter)
        {
            return _triggerEffects.Fire(triggerName, parameter);
        }

        public void Entered()
        {
            _definition.OnEntered(this);
        }

        public void Leaving(IStateTransition<TState> transition)
        {
            _definition.OnLeaving(transition);
        }

        public void GoingToNextState()
        {
            if(_actionWhenLeaving != null)
                _actionWhenLeaving();
        }

    }
}
